﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
using Baula.TextGen.Framework.Sections;
using Baula.TextGen.Framework.Tools;

namespace Baula.TextGen.Framework
{
    public class PartTemplate
    {
        private const string BeginToken = "' === ";

        #region Static Members

        public static bool IsBegin(string line)
        {
            return line.StartsWith(BeginToken);    // TODO: make more flexible (Regex)
        }

        #endregion

        private readonly LineCollector _lineCollector = new LineCollector();
        private List<string> _placeholders = new List<string>();

        #region Creators

        internal static PartTemplate CreateFromContent(string content)
        {
            var newPartTemplate = new PartTemplate();

            newPartTemplate.SetupFromContent(content);
            
            return newPartTemplate;
        }

        private void SetupFromContent(string content)
        {
            SectionsPrivate = new List<ISection>();
            Parse(content);
            _placeholders = ReadPlaceholders(content);
        }

        internal protected PartTemplate()
        {
            SectionsPrivate = new List<ISection>();
        }

        internal protected PartTemplate(string headingLine)
        {
            SectionsPrivate = new List<ISection>();
            Name = GetName(headingLine);
            CurrentSection = null;
        }

        private void Parse(string content)
        {
            var parser = new TemplateParser(false);
            parser.Parse(content);

            var enumerator = ((IDictionary<string, PartTemplate>)parser.PartTemplates).GetEnumerator();
            if (parser.RootPartTemplate.IsEmpty)
            {
                enumerator.MoveNext();
                var firstPartTemplate = enumerator.Current.Value;
                EnsureIsOnLastElement(enumerator);
                Name = firstPartTemplate.Name;
                SectionsPrivate = firstPartTemplate.SectionsPrivate;
            }
            else
            {
                EnsureIsOnLastElement(enumerator);
                SectionsPrivate = parser.RootPartTemplate.SectionsPrivate;
            }
        }

        private void EnsureIsOnLastElement(IEnumerator<KeyValuePair<string, PartTemplate>> enumerator)
        {
            if (enumerator.MoveNext())
            {
                throw new SubPartsNotSupportedException();
            }
        }

        private bool IsEmpty
        {
            get { return string.IsNullOrEmpty(Content); }
        }

        #endregion

        internal string Content
        {
            get { return LineCollector.ToString(); }
        }

        public string Name { get; private set; }

        internal bool IsRootPart
        {
            get { return string.IsNullOrEmpty(Name); }
        }

        internal void AppendLine(string line, bool isParsingDocumentTemplate)
        {
            LineCollector.AppendAsNewLine(line);

            ParseLine(line, isParsingDocumentTemplate);
        }

        private void ParseLine(string line, bool isParsingDocumentTemplate)
        {
            bool isMainPart = IsRootPart && isParsingDocumentTemplate;
            if (RelocationSection.IsBegin(line))
            {
                if (isMainPart)
                {
                    throw new RelocationSectionInvalidInMainPartException();
                }
                SaveCurrentSection();
                BeginRelocationSection(line);
            }
            else if (PlaceholderSection.IsBegin(line))
            {
                if (!isMainPart)
                {
                    throw new PlaceholderSectionInvalidOutsideMainPartException();
                }
                SaveCurrentSection();
                BeginPlaceholderSection(line);
                SaveCurrentSection();
                CurrentSection = null;  // This is a single line section
            }
            else
            {
                if (CurrentSection == null)
                {
                    CurrentSection = new ContentSection();
                }
                CurrentSection.AppendLine(line);
            }
        }

        private void BeginRelocationSection(string line)
        {
            var relocationSection = new RelocationSection(line);
            if (relocationSection.TargetSectionName == "End")
            {
                CurrentSection = new ContentSection();
            }
            else
            {
                CurrentSection = relocationSection;
            }
        }

        private void BeginPlaceholderSection(string line)
        {
            var newPlaceholderSection = new PlaceholderSection(line);
            if (newPlaceholderSection.Name == "End")
            {
                throw new UsageOfImplicitNameForPlaceholderSectionException(newPlaceholderSection.Name);
            }
            CurrentSection = newPlaceholderSection;
        }

        private void SaveCurrentSection()
        {
            if (CurrentSection != null)
            {
                SectionsPrivate.Add(CurrentSection);
            }
        }

        private string GetName(string firstLine)
        {
            if (!IsBegin(firstLine))
            {
                throw new InvalidPartHeadingLineException(firstLine);
            }

            return firstLine.Substring(BeginToken.Length);  // TODO make more flexible (Regex)
        }

        private LineCollector LineCollector
        {
            get { return _lineCollector; }
        }

        internal ReadOnlyCollection<string> Placeholders
        {
            get { return _placeholders.AsReadOnly(); }
        }

        internal ReadOnlyCollection<ISection> Sections
        {
            get { return SectionsPrivate.AsReadOnly(); }
        }

        private List<ISection> SectionsPrivate { get; set; }

        internal IDictionary<string, PlaceholderSection> PlaceholderSections
        {
            get
            {
                return Sections.OfType<PlaceholderSection>().ToDictionary(placeholderSection => placeholderSection.Name);
            }
        }

        private ISection CurrentSection { get; set; }

        internal void OnReachedLastLine()
        {
            SaveCurrentSection();

            _placeholders = ReadPlaceholders(Content);
        }

        private List<string> ReadPlaceholders(string content)
        {
            var placeholders = new List<string>();
            var regex = new Regex("%([^%:\r\n]*)%");
            foreach (Match match in regex.Matches(content))
            {
                var placeholder = GetPlaceholder(match.Value);
                if (!placeholders.Contains(placeholder))
                {
                    placeholders.Add(placeholder);
                }
            }

            return placeholders;
        }

        private string GetPlaceholder(string token)
        {
            return token.Substring(1, token.Length - 2);
        }

        public Part CreatePart()
        {
            return  Factory.CreatePart(this);
        }
    }
}